//
//  MPStore.m
//  MissPaparazzi
//
//  Created by Alexey Aleshkov on 02.09.12.
//  Copyright (c) 2012 DzeTech. All rights reserved.
//


#import "MPStore.h"
#import "MPEpisode.h"
#import "NSDate+StringWithFormat.h"
#import "MPStoreKit.h"
#import "ZipArchive+BlocksHelper.h"
#import "SFHFKeychainUtils.h"


static NSString *const kMPStoreBaseURLString = @"http://app.misspaparazzi.tv";
static NSString *const kMPStoreRequestPathString = @"products.json";
static NSString *const kMPStoreEpisodesFolder = @"Episodes";
static NSString *const kMPStoreArchivesFolder = @"Archives";

static NSString *const kMPStoreAlreadyLaunched = @"Already launched";

static NSString *const kMPStoreBuyAllProductID = @"com.prapp.misspaparazzi.spec.buyall";
static NSString *const kMPStoreBuyAllProductIDPrice = @"com.prapp.misspaparazzi.spec.buyall.price";
static NSString *const kMPStoreTestProductID = @"com.prapp.misspaparazzi.spec.consumable";


// Base URL - http://misspaparazzi.pr-app.com
// Episode URL - <Base URL>/<Episode Product ID>
// Episode ZIP - <Episode URL>/video.zip
// Episode Password - <Episode Product ID>+pr-app
// Episode PNG - <Episode URL>/image.png


@implementation MPStore

@synthesize managedObjectContext = _managedObjectContextStore;

+ (MPStore *)sharedInstance
{
	static id sharedInstance = nil;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		sharedInstance = [[MPStore alloc] init];
	});
	return sharedInstance;
}

#pragma mark -
#pragma mark Init / Dealloc

- (NSString *)applicationDocumentsDirectory
{
	NSString *result;
	result = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
	return result;
}

- (NSString *)applicationLibraryDirectory
{
	NSString *result;
	result = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
	return result;
}

- (NSString *)applicationCachesDirectory
{
	NSString *result;
	result = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
	return result;
}

- (id)init
{
	self = [super init];
	if (!self) {
		return nil;
	}


	// reset kMPStoreBuyAllProductID at first launch
	NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
	NSNumber *alreadyLaunched = [userDefaults objectForKey:kMPStoreAlreadyLaunched];
	if (![alreadyLaunched boolValue]) {
		[self setString:nil forKey:kMPStoreBuyAllProductID];
	}
	alreadyLaunched = [NSNumber numberWithBool:YES];
	[userDefaults setObject:alreadyLaunched forKey:kMPStoreAlreadyLaunched];
	[userDefaults synchronize];
	
	
	NSError *error = nil;
	

	NSFileManager *fileManager = [NSFileManager defaultManager];
	
	_episodesPath = [self applicationLibraryDirectory];
	_episodesPath = [_episodesPath stringByAppendingPathComponent:kMPStoreEpisodesFolder];
	[_episodesPath retain];
	[fileManager createDirectoryAtPath:_episodesPath withIntermediateDirectories:NO attributes:nil error:&error];
	
	_archivesPath = [self applicationCachesDirectory];
	_archivesPath = [_archivesPath stringByAppendingPathComponent:kMPStoreArchivesFolder];
	[_archivesPath retain];
	[fileManager createDirectoryAtPath:_archivesPath withIntermediateDirectories:NO attributes:nil error:&error];
	
	
	_reachability = [Reachability reachabilityForInternetConnection];
	[_reachability retain];

	
	NSString *baseURLString = kMPStoreBaseURLString;
	NSURL *baseURL = [NSURL URLWithString:baseURLString];
	_client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
	
	_downloader = [[MPDownloader alloc] initWithBaseURL:baseURL];
	
	
	NSString *path = [[NSBundle mainBundle] pathForResource:@"MPEpisode" ofType:@"momd"];
	NSURL *momURL = [NSURL fileURLWithPath:path];
	
	NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Episodes.sqlite"];
	NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
	_managedObjectModelStore = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
	_persistentStoreCoordinatorStore = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModelStore];
	_persistentStoreStore = [_persistentStoreCoordinatorStore addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
	[_persistentStoreStore retain];
	if (!_persistentStoreStore) {
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		abort();
	}
	_managedObjectContextStore = [[NSManagedObjectContext alloc] init];
	_managedObjectContextStore.persistentStoreCoordinator = _persistentStoreCoordinatorStore;

	return self;
}

- (void)dealloc
{
	_managedObjectContextStore.persistentStoreCoordinator = nil;
	[_managedObjectContextStore release];
	
	[_persistentStoreCoordinatorStore removePersistentStore:_persistentStoreStore error:nil];
	[_persistentStoreStore release];
	[_persistentStoreCoordinatorStore release];

	[_managedObjectModelStore release];
	
	[_downloader release];
	
	[_client release];
	
	[_reachability release];

	[_archivesPath release];
	[_episodesPath release];

	[super dealloc];
}

#pragma mark -
#pragma mark Private methods

- (NSString *)stringForKey:(NSString *)key
{
	NSError *error = nil;
	NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
	NSString *result = [SFHFKeychainUtils getPasswordForUsername:key andServiceName:bundleIdentifier error:&error];
	if (error) {
		NSLog(@"%@", [error localizedDescription]);
	}
	
	return result;
}

- (void)setString:(NSString *)string forKey:(NSString *)key
{
	NSError *error = nil;
	NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
	if (string) {
		[SFHFKeychainUtils storeUsername:key andPassword:string forServiceName:bundleIdentifier updateExisting:YES error:&error];
		if (error) {
			NSLog(@"%@", [error localizedDescription]);
		}
	} else {
		[SFHFKeychainUtils deleteItemForUsername:key andServiceName:bundleIdentifier error:&error];
		if (error) {
			NSLog(@"%@", [error localizedDescription]);
		}
	}
}

- (NSString *)passwordForItem:(MPEpisode *)item
{
	NSString *result;
	// must be like @"com.prapp.misspaparazzi.video.episode_01+pr-app"
	result = [item.productID stringByAppendingString:@"+pr-app"];
	// but now is
	//result = @"com.pr-app.misspaparazzi.episode_01+pr-app";
	return result;
}

- (NSString *)pathForItem:(MPEpisode *)item
{
	NSString *result;
	result = [NSString stringWithFormat:@"%@/%@", _episodesPath, item.productID];
	return result;
}

- (void)createItemPath:(MPEpisode *)item
{
	NSString *path = [self pathForItem:item];
	
	NSFileManager *fileManager = [NSFileManager defaultManager];
	[fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}

- (void)removeItemPath:(MPEpisode *)item
{
	NSString *path = [self pathForItem:item];
	
	NSFileManager *fileManager = [NSFileManager defaultManager];
	[fileManager removeItemAtPath:path error:nil];
}

- (void)downloadImageForItem:(MPEpisode *)item
{
	NSString *imageURLString = [self imageURLStringForItem:item];
	NSURL *imageURL = [NSURL URLWithString:imageURLString];
	
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:imageURL];
    [request setHTTPShouldHandleCookies:NO];
    [request setHTTPShouldUsePipelining:YES];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
	
	void (^successBlock)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject) {
		NSString *imagePath = [self imagePathForItem:item];
		NSError *error = nil;
		
		UIImage *image = responseObject;
		NSData *imageData = UIImagePNGRepresentation(image);
		[imageData writeToFile:imagePath atomically:YES];
		
		item.imageDownloaded = [NSNumber numberWithBool:YES];
		
		if (![_managedObjectContextStore save:&error]) {
			NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		}
	};
	void (^failureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^(AFHTTPRequestOperation *operation, NSError *error) {
		NSLog(@"%@", error);
	};
	AFImageRequestOperation *operation = [[AFImageRequestOperation alloc] initWithRequest:request];
	[operation setCompletionBlockWithSuccess:successBlock failure:failureBlock];
	[_client enqueueHTTPRequestOperation:operation];
	[operation release];
}

- (void)synchronizeWithJSONArray:(NSArray *)array andProducts:(NSArray *)products
{
	NSError *error = nil;
	
	// Remove
	{
		NSArray *arrayIDs = [array valueForKeyPath:@"id"];
		NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Episode"];
		fetchRequest.predicate = [NSPredicate predicateWithFormat:@"NOT (productID IN %@)", arrayIDs];
		
		NSArray *removeElements = [_managedObjectContextStore executeFetchRequest:fetchRequest error:&error];
		NSArray *arrayDeletedIDs = [removeElements valueForKeyPath:@"productID"];
		for (MPEpisode *object in removeElements) {
			[self removeItemPath:object];
			[_managedObjectContextStore deleteObject:object];
			
			[self removeItemPath:object];
		}
		
		NSPredicate *removeDeletedItemsPredicate = [NSPredicate predicateWithFormat:@"NOT (id IN %@)", arrayDeletedIDs];
		array = [array filteredArrayUsingPredicate:removeDeletedItemsPredicate];
	}
	// Update
	{
		NSArray *arrayIDs = [array valueForKeyPath:@"id"];
		NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Episode"];
		fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(productID IN %@)", arrayIDs];
		
		NSArray *updateElements = [_managedObjectContextStore executeFetchRequest:fetchRequest error:&error];
		NSArray *arrayUpdatedIDs = [updateElements valueForKeyPath:@"productID"];
		for (MPEpisode *object in updateElements) {
			NSPredicate *itemByIDPredicate = [NSPredicate predicateWithFormat:@"id == %@", object.productID];
			NSArray *filteredItems = [array filteredArrayUsingPredicate:itemByIDPredicate];
			NSAssert([filteredItems count], @"");
			NSDictionary *item = [filteredItems objectAtIndex:0];
			object.name = [item objectForKey:@"name"];
			object.fullText = [item objectForKey:@"description"];
			object.free = [item objectForKey:@"free"];
			object.size = [item objectForKey:@"size"];
			NSString *dateString = [item objectForKey:@"date"];
			object.date = [NSDate dateFromString:dateString withFormat:@"dd-MM-yyyy" andSecondsFromGMT:0];
			
			NSPredicate *productByIDPredicate = [NSPredicate predicateWithFormat:@"productIdentifier == %@", object.productID];
			NSArray *filteredProducts = [products filteredArrayUsingPredicate:productByIDPredicate];
			NSAssert([filteredProducts count], @"");
			SKProduct *product = [filteredProducts objectAtIndex:0];
			object.price = [NSString stringWithFormat:@"%@", product.price];
			
			if (!object.thumbnailImage) {
				[self downloadImageForItem:object];
			}
		}
		
		NSPredicate *removeUpdatedItemsPredicate = [NSPredicate predicateWithFormat:@"NOT (id IN %@)", arrayUpdatedIDs];
		array = [array filteredArrayUsingPredicate:removeUpdatedItemsPredicate];
	}
	// Add
	{
		for (NSDictionary *item in array) {
			MPEpisode *object;
			NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Episode" inManagedObjectContext:_managedObjectContextStore];
			object = [[MPEpisode alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:_managedObjectContextStore];
			[object autorelease];
			object.productID = [item objectForKey:@"id"];
			object.name = [item objectForKey:@"name"];
			object.fullText = [item objectForKey:@"description"];
			object.free = [item objectForKey:@"free"];
			object.size = [item objectForKey:@"size"];
			NSString *dateString = [item objectForKey:@"date"];
			object.date = [NSDate dateFromString:dateString withFormat:@"dd-MM-yyyy" andSecondsFromGMT:0];
			
			NSPredicate *productByIDPredicate = [NSPredicate predicateWithFormat:@"productIdentifier == %@", object.productID];
			NSArray *filteredProducts = [products filteredArrayUsingPredicate:productByIDPredicate];
			NSAssert([filteredProducts count], @"");
			SKProduct *product = [filteredProducts objectAtIndex:0];
			object.price = [NSString stringWithFormat:@"%@", product.price];
			
			[self createItemPath:object];
			
			[self downloadImageForItem:object];
		}
	}
	if (![_managedObjectContextStore save:&error]) {
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
	}
}

#pragma mark -
#pragma mark Self methods

- (NSString *)imageURLStringForItem:(MPEpisode *)item
{
	NSString *result;
	result = [NSString stringWithFormat:@"%@/%@/image.jpg", kMPStoreBaseURLString, item.productID];
	return result;
}

- (NSString *)videoURLStringForItem:(MPEpisode *)item
{
	NSString *result;
	result = [NSString stringWithFormat:@"%@/%@/video.zip", kMPStoreBaseURLString, item.productID];
	return result;
}

- (NSString *)imagePathForItem:(MPEpisode *)item
{
	NSString *result;
	result = [NSString stringWithFormat:@"%@/%@/image.jpg", _episodesPath, item.productID];
	return result;
}

- (NSString *)videoPathForItem:(MPEpisode *)item
{
	NSString *result;
	result = [NSString stringWithFormat:@"%@/%@/video.mp4", _episodesPath, item.productID];
	return result;
}

- (BOOL)isImageExists:(MPEpisode *)item
{
	NSFileManager *fileManager = [NSFileManager defaultManager];
	BOOL isDir;
	BOOL result = [fileManager fileExistsAtPath:[self imagePathForItem:item] isDirectory:&isDir] && !isDir;
	return result;
}

- (BOOL)isVideoExists:(MPEpisode *)item
{
	NSFileManager *fileManager = [NSFileManager defaultManager];
	BOOL isDir;
	BOOL result = [fileManager fileExistsAtPath:[self videoPathForItem:item] isDirectory:&isDir] && !isDir;
	return result;
}

- (void)updateWithSuccessBlock:(MPStoreSuccessBlock)successBlock failureBlock:(MPStoreFailureBlock)failureBlock
{
	MPStoreSuccessBlock localSuccessBlock = [successBlock copy];
	MPStoreFailureBlock localFailureBlock = [failureBlock copy];

	void (^success)(NSURLRequest *, NSHTTPURLResponse *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
		NSArray *contentArray = JSON;

		NSMutableArray *netObjectIds = [NSMutableArray arrayWithArray:[contentArray valueForKeyPath:@"id"]];
		[netObjectIds addObject:kMPStoreBuyAllProductID];
		//NSLog(@"AFJSONRequestOperation %@", netObjectIds);

		MPStoreKitProductsRequestSuccessBlock successBlock = ^(NSArray *products, NSArray *invalidProductIdentifiers) {
			NSArray *skObjectIds = [products valueForKeyPath:@"productIdentifier"];
			//NSLog(@"SKProductsRequestSuccessBlock %@", skObjectIds);
			
			NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:@"id IN %@", skObjectIds];
			// new objects from net
			NSArray *filteredObjects = [contentArray filteredArrayUsingPredicate:intersectPredicate];
			//NSLog(@"SKProductsRequestSuccessBlock %@", filteredObjects);
			
			[self synchronizeWithJSONArray:filteredObjects andProducts:products];
			
			NSPredicate *buyAllProductIDPredicate = [NSPredicate predicateWithFormat:@"productIdentifier == %@", kMPStoreBuyAllProductID];
			NSArray *buyAllProducts = [products filteredArrayUsingPredicate:buyAllProductIDPredicate];
			NSAssert([buyAllProducts count], @"");
			SKProduct *buyAllProduct = [buyAllProducts objectAtIndex:0];
			NSString *buyAllProductPrice = [NSString stringWithFormat:@"%@", buyAllProduct.price];
			[self setString:buyAllProductPrice forKey:kMPStoreBuyAllProductIDPrice];

			BOOL allItemsPurchased = !![[self stringForKey:kMPStoreBuyAllProductID] length];
			if (allItemsPurchased) {
				NSError *error = nil;
				NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Episode"];
				NSArray *array = [_managedObjectContextStore executeFetchRequest:fetchRequest error:&error];
				for (MPEpisode *item in array) {
					item.wasPurchased = [NSNumber numberWithBool:YES];
				}
				if (![_managedObjectContextStore save:&error]) {
					NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
				}
			}
			
			if (localSuccessBlock) {
				localSuccessBlock();
			}

			[localFailureBlock release];
			[localSuccessBlock release];
		};
		MPStoreKitProductsRequestFailureBlock failureBlock = ^(NSError *error) {
			//NSLog(@"SKProductsRequestFailureBlock %@", error);
			if (localFailureBlock) {
				localFailureBlock(error);
			}

			[localFailureBlock release];
			[localSuccessBlock release];
		};
		[[MPStoreKit sharedInstance] requestProductsWithProductIdentifiers:netObjectIds successBlock:successBlock failureBlock:failureBlock];
	};
	void (^failure)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
		NSLog(@"failure");
		if (localFailureBlock) {
			localFailureBlock(error);
		}
		
		[localFailureBlock release];
		[localSuccessBlock release];
	};
	
	NSURLRequest *request = [_client requestWithMethod:@"GET" path:kMPStoreRequestPathString parameters:nil];
	AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:success failure:failure];
	
	[_client enqueueHTTPRequestOperation:operation];
}

- (void)restorePurchasesWithSuccessBlock:(MPStoreSuccessBlock)successBlock failureBlock:(MPStoreFailureBlock)failureBlock
{
	MPStoreSuccessBlock localSuccessBlock = [successBlock copy];
	MPStoreFailureBlock localFailureBlock = [failureBlock copy];
	
	MPStoreKitRestoreTransactionsSuccessBlock restoreSuccessBlock = ^(NSArray *productIdentifiers) {
		NSLog(@"MPStoreKitRestoreTransactionsSuccessBlock %@", productIdentifiers);
		
		NSError *error = nil;

		NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Episode"];

		NSPredicate *buyAllProductIDPredicate = [NSPredicate predicateWithFormat:@"SELF == %@", kMPStoreBuyAllProductID];
		NSArray *buyAllProductIDs = [productIdentifiers filteredArrayUsingPredicate:buyAllProductIDPredicate];
		BOOL allItemsPurchased = !![buyAllProductIDs count];
		if (allItemsPurchased) {
			[self setString:@"1" forKey:kMPStoreBuyAllProductID];
		} else {
			fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(productID IN %@)", productIdentifiers];
		}
		
		NSArray *array = [_managedObjectContextStore executeFetchRequest:fetchRequest error:&error];
		for (MPEpisode *item in array) {
			item.wasPurchased = [NSNumber numberWithBool:YES];
		}
		
		if (![_managedObjectContextStore save:&error]) {
			NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		}

		if (localSuccessBlock) {
			localSuccessBlock();
		}
		
		[localFailureBlock release];
		[localSuccessBlock release];
	};
	MPStoreKitRestoreTransactionsFailureBlock restoreFailureBlock = ^(NSError *error) {
		NSLog(@"MPStoreKitRestoreTransactionsFailureBlock %@", error);
		if (localFailureBlock) {
			localFailureBlock(error);
		}
		
		[localFailureBlock release];
		[localSuccessBlock release];
	};
	[[MPStoreKit sharedInstance] restoreTransactionsWithSuccessBlock:restoreSuccessBlock progressBlock:nil failureBlock:restoreFailureBlock];
}

- (void)purchaseAllItemsWithSuccessBlock:(MPStoreSuccessBlock)successBlock failureBlock:(MPStoreFailureBlock)failureBlock
{
	MPStoreSuccessBlock localSuccessBlock = [successBlock copy];
	MPStoreFailureBlock localFailureBlock = [failureBlock copy];
	
	MPStoreKitBuyFeatureSuccessBlock buyFeatureSuccessBlock = ^(NSString *productIdentifier) {
		[self setString:@"1" forKey:kMPStoreBuyAllProductID];
		
		NSError *error = nil;
		
		NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Episode"];
		NSArray *array = [_managedObjectContextStore executeFetchRequest:fetchRequest error:&error];
		for (MPEpisode *item in array) {
			item.wasPurchased = [NSNumber numberWithBool:YES];
		}
		[_managedObjectContextStore save:nil];

		if (localSuccessBlock) {
			localSuccessBlock();
		}
		[localSuccessBlock release];
		[localFailureBlock release];
	};
	MPStoreKitBuyFeatureFailureBlock buyFeatureFailureBlock = ^(NSString *productIdentifier, NSError *error) {
		if (localFailureBlock) {
			localFailureBlock(error);
		}
		[localSuccessBlock release];
		[localFailureBlock release];
	};
	[[MPStoreKit sharedInstance] buyFeature:kMPStoreBuyAllProductID successBlock:buyFeatureSuccessBlock failureBlock:buyFeatureFailureBlock];
}

- (void)purchaseItem:(MPEpisode *)item successBlock:(MPStoreSuccessBlock)successBlock failureBlock:(MPStoreFailureBlock)failureBlock
{
	MPStoreSuccessBlock localSuccessBlock = [successBlock copy];
	MPStoreFailureBlock localFailureBlock = [failureBlock copy];
	MPEpisode *localItem = [item retain];
	
	MPStoreKitBuyFeatureSuccessBlock buyFeatureSuccessBlock = ^(NSString *productIdentifier) {
		NSError *error = nil;

		localItem.wasPurchased = [NSNumber numberWithBool:YES];
		if (![_managedObjectContextStore save:&error]) {
			NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		}

		if (localSuccessBlock) {
			localSuccessBlock();
		}
		[localItem release];
		[localSuccessBlock release];
		[localFailureBlock release];
	};
	MPStoreKitBuyFeatureFailureBlock buyFeatureFailureBlock = ^(NSString *productIdentifier, NSError *error) {
		if (localFailureBlock) {
			localFailureBlock(error);
		}
		[localItem release];
		[localSuccessBlock release];
		[localFailureBlock release];
	};
	[[MPStoreKit sharedInstance] buyFeature:localItem.productID successBlock:buyFeatureSuccessBlock failureBlock:buyFeatureFailureBlock];
}

- (void)downloadItem:(MPEpisode *)item successBlock:(MPStoreSuccessBlock)successBlock failureBlock:(MPStoreFailureBlock)failureBlock
{
	if (!item.wasPurchased.boolValue && !item.free.boolValue) {
		return;
	}
	
	if ([self isDownloaded:item]) {
		NSError *error = nil;
		item.downloadState = MPEpisodeDownloadDownloaded;
		item.downloaded = [NSNumber numberWithBool:YES];
		if (![_managedObjectContextStore save:&error]) {
			NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		}
		if (successBlock) {
			successBlock();
		}
		return;
	}
	
	if (![_reachability isReachableViaWiFi]) {
		NSString *errorDomain = NSLocalizedString(@"MainView.NoWiFiConnection", nil);
		NSError *error = [NSError errorWithDomain:errorDomain code:-1 userInfo:nil];
		if (failureBlock) {
			failureBlock(error);
		}
		return;
	}
	
	MPStoreSuccessBlock localSuccessBlock = [successBlock copy];
	MPStoreFailureBlock localFailureBlock = [failureBlock copy];
	MPEpisode *localItem = [item retain];
	
	NSString *productIdentifier = item.productID;
	NSString *URLString = [self videoURLStringForItem:item];
	NSString *archivePath = [NSString stringWithFormat:@"%@/%@.zip", _archivesPath, productIdentifier];
	NSString *episodePath = [NSString stringWithFormat:@"%@/", [self pathForItem:item]];
	NSString *password = [self passwordForItem:item];

	MPDownloaderProgressBlock downloaderProgressBlock = ^(NSString *URLString, UInt64 readBytes, UInt64 totalBytes) {
		localItem.totalBytes = totalBytes;
		localItem.readBytes = readBytes;
	};
	MPDownloaderSuccessBlock downloaderSuccessBlock = ^(NSString *URLString, NSString *targetPath) {
		ZipArchiveUnpackSuccessBlock archiveSuccessBlock = ^(void) {
			NSError *error = nil;
			
			NSString *size = [NSString stringWithFormat:@"%lld MB", localItem.totalBytes / 1024 / 1024];
			localItem.downloadState = MPEpisodeDownloadDownloaded;
			localItem.downloaded = [NSNumber numberWithBool:YES];
			localItem.size = size;
			
			if (![_managedObjectContextStore save:&error]) {
				NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
			}

			NSFileManager *fileManager = [NSFileManager defaultManager];
			[fileManager removeItemAtPath:archivePath error:nil];

			if (localSuccessBlock) {
				localSuccessBlock();
			}
			
			[localItem release];
			[localSuccessBlock release];
			[localFailureBlock release];
		};
		ZipArchiveUnpackFailureBlock archiveFailureBlock = ^(void) {
			localItem.downloadState = MPEpisodeDownloadNone;
			
			NSFileManager *fileManager = [NSFileManager defaultManager];
			[fileManager removeItemAtPath:archivePath error:nil];

			if (localFailureBlock) {
				localFailureBlock(nil);
			}
			[localItem release];
			[localSuccessBlock release];
			[localFailureBlock release];
		};
		localItem.downloadState = MPEpisodeDownloadProcessing;
		[ZipArchive unpackFile:archivePath toFolder:episodePath usingPassword:password successBlock:archiveSuccessBlock failureBlock:archiveFailureBlock];
	};
	MPDownloaderFailureBlock downloaderFailureBlock = ^(NSString *URLString, NSString *targetPath, NSError *error) {
		localItem.downloadState = MPEpisodeDownloadNone;
		if (localFailureBlock) {
			localFailureBlock(error);
		}
		[localItem release];
		[localSuccessBlock release];
		[localFailureBlock release];
	};
	localItem.downloadState = MPEpisodeDownloadDownloading;
	[_downloader downloadFile:URLString toPath:archivePath successBlock:downloaderSuccessBlock failureBlock:downloaderFailureBlock progressBlock:downloaderProgressBlock];
}

- (void)cancelDownloadingItem:(MPEpisode *)item
{
	NSString *URLString = [self videoURLStringForItem:item];
	[_downloader cancel:URLString];
	item.downloadState  = MPEpisodeDownloadNone;
	item.totalBytes     = 1;
	item.readBytes      = 0;
}

- (void)removeItem:(MPEpisode *)item
{
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSString *path = [self videoPathForItem:item];
	[fileManager removeItemAtPath:path error:nil];

	item.downloadState = MPEpisodeDownloadNone;
	item.downloaded = [NSNumber numberWithBool:NO];
	[_managedObjectContextStore save:nil];
}

- (BOOL)isDownloaded:(MPEpisode *)item
{
	BOOL result;
	result = [self isVideoExists:item];
	return result;
}

- (NSString *)buyAllProductPrice
{
	NSString *result;
	result = [self stringForKey:kMPStoreBuyAllProductIDPrice];
	return result;
}

- (void)pauseAllDownloads
{
	[_downloader pauseAll];
}

- (void)resumeAllDownloads
{
	[_downloader resumeAll];
}

@end
